//
// Created by Soar Qin on 2017/8/22.
// Copyright (c) 2017 Soar Qin. All rights reserved.
//

#include "common.hh"

#include "ip_addr.hh"

#include <event2/util.h>

namespace core::async {

struct sockaddr_storage;

IPAddr::IPAddr(const std::string &address) {
    int size = sizeof(addr_);
    auto *addr = (struct sockaddr *)&addr_[0];
    bool hostIsEmpty = address.length() > 1 && address[0] == ':' && address[1] != ':';
    ok_ = evutil_parse_sockaddr_port((hostIsEmpty ? ("0.0.0.0" + address) : address).c_str(), addr, &size) == 0;
    isv6_ = ok_ && addr->sa_family == AF_INET6;
}

IPAddr::IPAddr(const std::string &ip, uint16_t port) {
    int size = sizeof(addr_);
    auto *addr = (struct sockaddr *)&addr_[0];
    ok_ = evutil_parse_sockaddr_port(ip.c_str(), addr, &size) == 0;
    if (!ok_) return;
    isv6_ = addr->sa_family == AF_INET6;
    if (isv6_)
        ((struct sockaddr_in6*)addr)->sin6_port = htons(port);
    else
        ((struct sockaddr_in*)addr)->sin_port = htons(port);
}

const struct sockaddr *IPAddr::getAddr(int &size) const {
    size = isv6_ ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
    return (const struct sockaddr *)&addr_[0];
}

std::tuple<struct sockaddr *, int> IPAddr::mutableAddr() {
    return std::make_tuple((struct sockaddr *)&addr_[0], sizeof(addr_));
}

void IPAddr::set(const struct sockaddr *addr) {
    ok_ = false;
    isv6_ = false;
    switch (addr->sa_family) {
    case AF_INET:
        ok_ = true;
        memcpy(addr_, addr, sizeof(struct sockaddr_in));
        break;
    case AF_INET6:
        ok_ = true;
        isv6_ = true;
        memcpy(addr_, addr, sizeof(struct sockaddr_in6));
        break;
    default: break;
    }
}

bool IPAddr::operator==(const IPAddr &other) const {
    const auto *saddr = (const struct sockaddr*)addr_;
    const auto *saddr2 = (const struct sockaddr*)other.addr_;
    if (saddr->sa_family != saddr2->sa_family) return false;
    switch(saddr->sa_family) {
    case AF_INET: {
        const auto *addr = (const struct sockaddr_in*)saddr;
        const auto *addr2 = (const struct sockaddr_in*)saddr2;
        return addr->sin_port == addr2->sin_port && addr->sin_addr.s_addr == addr2->sin_addr.s_addr;
    }
    case AF_INET6: {
        const auto *addr = (const struct sockaddr_in6*)saddr;
        const auto *addr2 = (const struct sockaddr_in6*)saddr2;
        if (addr->sin6_port != addr2->sin6_port) return false;
        return memcmp(addr->sin6_addr.s6_addr, addr2->sin6_addr.s6_addr, 16) == 0;
    }
    default:
        return memcmp(saddr, saddr2, sizeof(addr_)) == 0;
    }
}

size_t IPAddrHash::operator()(const IPAddr &s) const {
    static std::hash<size_t> _hasher;
    const auto *saddr = (const struct sockaddr*)s.addr_;
    switch(saddr->sa_family) {
    case AF_INET: {
        const auto *addr = (const struct sockaddr_in*)saddr;
        return _hasher(((size_t)addr->sin_port << (sizeof(size_t) * 8 - 16)) ^ (size_t)addr->sin_addr.s_addr);
    }
    case AF_INET6: {
        const auto *addr = (const struct sockaddr_in6*)saddr;
        size_t n = (size_t)addr->sin6_port << 16;
        size_t *v = (size_t*)&addr->sin6_addr.s6_addr;
        for(size_t i = 0; i < 16 / sizeof(size_t); ++i)
            n ^= *v++;
        return _hasher(n);
    }
    default:
        return _hasher(*(size_t*)saddr);
    }
}

}
